package com.base.library.base

import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.ViewTreeLifecycleOwner
import com.base.library.BR
import com.base.library.R
import com.base.library.ext.getThemeColor
import com.base.library.ext.listenTo
import com.base.library.ext.showToast
import com.base.library.utils.BarUtils
import com.base.library.utils.KeyboardUtils
import com.base.library.utils.ShareViewModelManager
import com.base.library.utils.ThemeUtils
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import java.lang.reflect.Field
import java.lang.reflect.ParameterizedType


abstract class BaseBottomSheetDialogFragment<VM : BaseVM, VB : ViewDataBinding> :
    BottomSheetDialogFragment() {
    protected lateinit var mVm: VM
    protected lateinit var mVb: VB
    private var mLoadingDialog: LoadingDialog? = null
    private val mModelClass by lazy {
        val parameterizedType = javaClass.genericSuperclass as ParameterizedType
        parameterizedType.actualTypeArguments[0] as Class<VM>
    }
    protected var mBehavior: BottomSheetBehavior<FrameLayout>? = null

    /**
     * 共享viewModel的key
     */
    open fun getShareViewModelKey(): String? = null

    open fun getViewModelStoreOwner(): ViewModelStoreOwner {
        return ShareViewModelManager.getShareViewOwner(getShareViewModelKey()) ?: this
    }

    override fun getTheme(): Int {
        return R.style.TransparentBottomSheetStyle
    }

    open fun getNavColor() = R.attr.deepColor1.getThemeColor()

    override fun onStart() {
        super.onStart()
        if (mBehavior == null) {
            dialog?.findViewById<FrameLayout?>(com.google.android.material.R.id.design_bottom_sheet)
                ?.let {
                    mBehavior = BottomSheetBehavior.from(it)
                }
        }
        mBehavior?.state = BottomSheetBehavior.STATE_EXPANDED
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View? {
        initViewBinding(inflater)
        generateViewModel()
        return mVb.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        dialog?.window?.let {
            BarUtils.setNavBarColor(it, getNavColor())
        }
        listenToViewModelEvents(mVm)
        lifecycle.addObserver(mVm)
        mVb.setVariable(getViewModelId(), mVm)
        mVm.arguments = arguments
        mVm.initData()
    }

    open fun generateViewModel() {
        mVm = ViewModelProvider(getViewModelStoreOwner())[mModelClass]
        ShareViewModelManager.putSharedViewModel(getShareViewModelKey(), this)
    }

    private fun initViewBinding(inflater: LayoutInflater) {
        mVb = DataBindingUtil.inflate(inflater, getLayoutId(), null, false)
        mVb.lifecycleOwner = this
        ViewTreeLifecycleOwner.set(mVb.root, this)
    }

    private fun listenToViewModelEvents(viewModel: VM) {
        viewModel.mStartActivityFlow.listenTo(lifecycle) {
            it.startActivityForResult(this)
        }
        viewModel.mToastContentFlow.listenTo(lifecycle) {
            toast(it)
        }
        viewModel.isClosedSelf.listenTo(lifecycle) {
            if (it == true) {
                dismissAllowingStateLoss()
            }
        }
        viewModel.mHideKeyboard.listenTo(lifecycle) {
            if (it == true) {
                KeyboardUtils.hideSoftInput(dialog?.window)
            }
        }
        viewModel.mLoadingDialogFlow.listenTo(lifecycle) {
            if (TextUtils.isEmpty(it)) {
                mLoadingDialog?.dismissAllowingStateLoss()
                return@listenTo
            }
            if (mLoadingDialog?.dialog?.isShowing == true) {
                return@listenTo
            }
            mLoadingDialog = LoadingDialog()
            mLoadingDialog?.show(childFragmentManager, "LoadingDialog")
        }
    }

    open fun toast(msg: CharSequence?) {
        msg?.let {
            context?.showToast(it)
        }
    }

    @Deprecated("Deprecated in Java")
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        mVm.onActivityResult(
            code = requestCode, result = data, resultCode = resultCode
        )
    }
    override fun show(manager: FragmentManager, tag: String?) {
        showAllowingStateLoss(manager, tag)
    }

    private fun showAllowingStateLoss(manager: FragmentManager, tag: String?) {
        try {
            val dismissed: Field = DialogFragment::class.java.getDeclaredField("mDismissed")
            dismissed.isAccessible = true
            dismissed.set(this, false)
        } catch (e: NoSuchFieldException) {
            e.printStackTrace()
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        }
        try {
            val shown: Field = DialogFragment::class.java.getDeclaredField("mShownByMe")
            shown.isAccessible = true
            shown.set(this, true)
        } catch (e: NoSuchFieldException) {
            e.printStackTrace()
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        }
        val ft: FragmentTransaction = manager.beginTransaction()
        ft.add(this, tag)
        ft.commitAllowingStateLoss()
    }
    override fun onDestroyView() {
        super.onDestroyView()
    }

    open fun getViewModelId(): Int = BR.vm

    abstract fun getLayoutId(): Int
}